<#include "license.ftl">
<@license/>
<#assign object = doc.object>
package ${object.@package}.service.base;

import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.io.PrintWriter;
<#if doc["/object/attributes/date/finder"][0]??>
import java.util.Date;
</#if>
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Set;
import java.util.Map;
import java.util.logging.Logger;
import redora.exceptions.*;
import redora.service.ServiceBase;
import redora.db.Statement;
import ${object.@package}.model.*;
import ${object.@package}.model.fields.${object.@name}Fields;
<#if doc["//enum[@scope='global']"][0]??>
import ${object.@package}.model.enums.*;
</#if>
import redora.util.JSONWriter;

import ${object.@package}.service.*;
import redora.api.fetch.*;

import static ${object.@package}.sql.${object.@name}SQL.*;
import static ${object.@package}.service.${object.@name}Util.jsonStream;
import static java.util.logging.Level.SEVERE;
import static redora.db.SQLParameter.*;

/**
 * @author Redora (www.redora.net)
*/
public class ${object.@name}ServiceJSONBase extends ServiceBase {
    private static final transient Logger l = Logger.getLogger("${object.@package}.service.${object.@name}ServiceJSON");

    protected ${object.@name}ServiceJSONBase() throws ConnectException {
        super("${object.schema[0]!"default"}");
    }
    protected ${object.@name}ServiceJSONBase(ServiceBase chain) {
        super(chain, "${object.schema[0]!"default"}");
        inTransaction = true; //Let the calling service handle the transaction
    }

    /**
    * @param id   (Mandatory) The id of the object you want to retrieve
    * @param scope (Mandatory) The allowed fetch scope: Scope.Table, Scope.Form, Scope.List.
    * @param writer  (Mandatory) Probably from the Servlet's output stream.
    * @throws ObjectNotFoundException When this object is not in the database
    */
    public void findById(@NotNull Long id, @NotNull Scope scope, @NotNull JSONWriter writer)
            throws QueryException, CopyException, ObjectNotFoundException, PagingException {
        String SQL;
        if (scope == Scope.Table) {
            SQL = FIND_BY_ID_TABLE;
        } else if (scope == Scope.List) {
            SQL = FIND_BY_ID_LIST;
        } else if (scope == Scope.Form) {
           SQL = FIND_BY_ID_FORM;
        } else {
            throw new PagingException("Illegal scope for this finder: " + scope);
        }
        SQL = prepare(SQL, id);
        ResultSet rs = null;
        try {
            rs = st.st.executeQuery(SQL);
            writer.responseStart();
            if (rs.next()) {
                jsonStream(rs, 0, writer, scope);
            } else {
                throw new ObjectNotFoundException("Failed to find ${object.@name}, using " + SQL);
            }
            writer.responseEnd();
        } catch (SQLException e) {
            l.log(SEVERE, "Failed to run query " + SQL, e);
                throw new QueryException("Failed to run query: " + SQL, e);
        } finally {
            close(rs);
        }
    }

    /**
    * Retrieves a bunch of pojo's at once. This method will fire per scope a query to the DB.
    * therefor you must deliver the id's in a map organized on Scope.
    * @param ids   (Mandatory) The ids of the objects you want to retrieve
    * @param writer  (Mandatory) Probably from the Servlet's output stream.
    */
    public void findById(@NotNull Map<Scope, Set<Long>> ids, @NotNull JSONWriter writer)
            throws QueryException, CopyException {
        ResultSet rs = null;
        String SQL = null;
        writer.responseStart();
        writer.print('[');
        try {
            for (Scope scope : ids.keySet()) {
                if (!ids.get(scope).isEmpty()) {
                    if (scope == Scope.Form) {
                        SQL = FIND_IN_ID_FORM;
                    } else if (scope == Scope.Table) {
                        SQL = FIND_IN_ID_TABLE;
                    } else if (scope == Scope.List) {
                        SQL = FIND_IN_ID_LIST;
                    } else {
                       throw new IllegalArgumentException("Illegal scope " + scope);
                    }
                    SQL = prepare(SQL, ids.get(scope));
                    rs = st.st.executeQuery(SQL);

                    char comma = ' ';
                    while (rs.next()) {
                        writer.print(comma);
                        jsonStream(rs, 0, writer, scope);
                        comma = ',';
                    }
                }
            }
        } catch(SQLException e) {
            l.log(SEVERE, "Failed to run query: " + SQL, e);
            throw new QueryException("Failed to run query: " + SQL, e);
        } finally {
            close(rs);
        }
        writer.responseWithArrayEnd();
    }

    public void finder(@NotNull DefaultFinder finder, @Nullable Object param, @NotNull Page page, @NotNull JSONWriter writer)
            throws QueryException, CopyException, PagingException {
        if (!(page.getScope() == Scope.List || page.getScope() == Scope.Table)) {
            throw new PagingException("Illegal scope (" + page.getScope() + ") for this finder: " + finder);
        }
        String SQL = page.getScope() == Scope.Table ? finder.sqlTable : finder.sqlList;
        if (finder != DefaultFinder.FindAll) {
            Object[] params = param.toString().split(",");
            for (Object s : params) {
                SQL = prepare(SQL, s);
            }
        }
        SQL = preparePage(SQL, page);
        ResultSet rs = null;
        try {
            rs = st.st.executeQuery(SQL);
            writer.responseWithPageStart(page);
            char comma = ' ';
            while (rs.next()) {
                writer.print(comma);
                jsonStream(rs, 0, writer, page.getScope());
                comma = ',';
            }
            writer.responseWithArrayEnd();
        } catch (SQLException e) {
            l.log(SEVERE, "Failed to run query " + SQL, e);
            throw new QueryException("Failed to run query: " + SQL, e);
        } finally {
            close(rs);
        }
    }

<#if object.lazyScope[0]??>
    public void fetchLazy(Long id, JSONWriter writer) throws ConnectException, QueryException, CopyException {
        String SQL = prepare(FETCH_LAZY_BY_ID, id);
        ResultSet rs = null;
        try {
            rs = st.st.executeQuery(SQL);
            writer.responseStart();
            if (rs.next()) {
                jsonStream(rs, 0, writer, Scope.Lazy);
            }
            writer.responseEnd();
        } catch(SQLException e) {
            l.log(SEVERE, "Failed to run query " + SQL, e);
            throw new QueryException("Failed to run query: " + SQL, e);
        } finally {
            close(rs);
        }
    }
</#if>
}
